package cz.cvut.fel.taskpro.business.manager;

import cz.cvut.fel.taskpro.business.imanager.IAssignmentBusiness;
import cz.cvut.fel.taskpro.business.isupport.IBusinessRuleValidator;
import cz.cvut.fel.taskpro.business.isupport.IMail;
import cz.cvut.fel.taskpro.business.support.BusinessRuleException;
import cz.cvut.fel.taskpro.business.support.Maintance;
import cz.cvut.fel.taskpro.business.support.TaskProException;
import cz.cvut.fel.taskpro.data.entity.Assignment;
import cz.cvut.fel.taskpro.data.entity.Assignment.Type;
import cz.cvut.fel.taskpro.data.entity.AssignmentSubmit;
import cz.cvut.fel.taskpro.data.entity.AssignmentSubmit.State;
import cz.cvut.fel.taskpro.data.entity.GroupTaskPro;
import cz.cvut.fel.taskpro.data.entity.Kantor;
import cz.cvut.fel.taskpro.data.entity.Student;
import cz.cvut.fel.taskpro.data.entity.Subject;
import cz.cvut.fel.taskpro.data.entity.Team;
import cz.cvut.fel.taskpro.data.entity.Template;
import cz.cvut.fel.taskpro.data.entity.TemplateFile;
import cz.cvut.fel.taskpro.data.entity.Version;
import cz.cvut.fel.taskpro.data.imanager.IAssignmentData;
import cz.cvut.fel.taskpro.data.imanager.IGroupData;
import cz.cvut.fel.taskpro.data.imanager.ITeamData;
import cz.cvut.fel.taskpro.data.imanager.IUserData;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.inject.Inject;

/**
 * This class implement #{@link IAssignmentBusiness} interface
 * @author Tom
 * @version 1.0 beta
 */
@DeclareRoles({"admin", "kantor", "student"})
@Stateless
public class AssignmentBusiness implements IAssignmentBusiness, Serializable {

    @Inject
    private IAssignmentData assignmentData;
    @Inject
    private IGroupData groupData;
    @Inject
    private ITeamData teamData;
    @Inject
    private IUserData userData;
    @EJB
    private IBusinessRuleValidator validator;
    @EJB
    private IMail mail;
    public AssignmentBusiness() {
    }

    
    @PermitAll
    @Override
    public List<Template> getTemplateByKantor(Kantor kantor) {
        List<Template> templates = assignmentData.getTemplatesByKantor(kantor);
        return templates;
    }

    @RolesAllowed("kantor")
    @Override
    public void modifyTemplate(Template template, List<TemplateFile> originFiles) throws BusinessRuleException {
        validator.validateUpdate(template);
        List<TemplateFile> newTemplateFiles = template.getTemplateFiles();
        Iterator templateFileIt = originFiles.iterator();
        while (templateFileIt.hasNext()) {
            TemplateFile currTemplate = (TemplateFile) templateFileIt.next();
            if (!newTemplateFiles.contains(currTemplate)) {
                currTemplate.setTemplate(null);
                assignmentData.removeTemplateFile(currTemplate);
            }
        }
        assignmentData.updateTemplate(template);
    }

    @RolesAllowed("kantor")
    @Override
    public void addTemplate(Template newTemplate) throws BusinessRuleException {
        validator.validate(newTemplate);
        newTemplate.setDateOfCreation(Maintance.getTodayDate());
        assignmentData.addTemplate(newTemplate);
    }

    @RolesAllowed("kantor")
    @Override
    public void createAssignments(List<Assignment> assignments, boolean notifyUsers) throws BusinessRuleException, TaskProException {
        Iterator itAs = assignments.iterator();
        List<Student> studentsToNotify = new ArrayList<Student>();
        while (itAs.hasNext()) {
            Assignment curraAssignment = (Assignment) itAs.next();
            validator.validate(curraAssignment);
            if (notifyUsers) {
                if (curraAssignment.getType() == Assignment.Type.GROUP) {
                    studentsToNotify.addAll(userData.getStudentsByGroup(curraAssignment.getmGroups()));
                } else if (curraAssignment.getType() == Assignment.Type.TEAM) {
                    studentsToNotify.addAll(userData.getStudentsByTeam(curraAssignment.getmTeams()));

                } else if (curraAssignment.getType() == Assignment.Type.STUDENT) {
                    studentsToNotify.addAll(curraAssignment.getmStudents());
                }
            }
        }
        assignmentData.assignTemplate(assignments);
        if (notifyUsers) {
            mail.notifyNewAssignment(studentsToNotify, assignments);
        }
       
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<Assignment> getAssignmentsToSubmit(Student student) throws BusinessRuleException {
        validator.validate(student);
        return assignmentData.assignmentToSubmit(student);
    }

    @RolesAllowed("student")
    @Override
    public void submitAssignments(Version versionOfTask) throws BusinessRuleException, TaskProException {
        validator.validate(versionOfTask);
         Team submittedTeam=null;
        AssignmentSubmit curAssignmentSubmit = versionOfTask.getAssignmentSubmit();
        validator.validateSubmitTask(curAssignmentSubmit);
        Date actualDate = Maintance.getTodayDate();
        versionOfTask.setDateOfSubmit(actualDate);
        versionOfTask.setNumberOfVersion(1);
        versionOfTask.setSubmittedStudent(curAssignmentSubmit.getStudent());
       
        curAssignmentSubmit.setDateOfSubmit(actualDate);
        versionOfTask.setAssignmentSubmit(curAssignmentSubmit);
        curAssignmentSubmit.addVersion(versionOfTask);
        curAssignmentSubmit.setStateOfTask(AssignmentSubmit.State.SUBMITTED);
        curAssignmentSubmit.setDateOfSubmit(Maintance.getTodayDate());
          Student assignmentStudent = curAssignmentSubmit.getStudent();
          assignmentStudent.addVersion(versionOfTask);
        if (curAssignmentSubmit.getAssignment().getType() == Assignment.Type.TEAM) {
            GroupTaskPro studentGroup = groupData.getGroupBySubjectAndStudent(curAssignmentSubmit.getAssignment().getSubject(), curAssignmentSubmit.getStudent());
            submittedTeam = teamData.getTeamByStudentAndGroup(curAssignmentSubmit.getStudent(), studentGroup);
            curAssignmentSubmit.setTeam(submittedTeam);
        }
        
        if (curAssignmentSubmit.getAssignment().getType() == Assignment.Type.TEAM) {
           assignmentStudent = curAssignmentSubmit.getStudent();
            List<AssignmentSubmit> result = assignmentData.getTeamAssignmentSubmitByContext(curAssignmentSubmit.getAssignment(),
                     submittedTeam, null);
            if (result.isEmpty()) {
                assignmentData.addAssignmentSubmit(curAssignmentSubmit);
                submittedTeam.addAssignmentSubmit(curAssignmentSubmit);
                teamData.updateTeam(submittedTeam);
            } else {
                AssignmentSubmit oldAssignmentSubmit = result.get(0);
                Version newVersion = curAssignmentSubmit.getmVersions().get(0);
                oldAssignmentSubmit.setDateOfSubmit(curAssignmentSubmit.getDateOfSubmit());
                oldAssignmentSubmit.setStateOfTask(curAssignmentSubmit.getStateOfTask());
                newVersion.setAssignmentSubmit(oldAssignmentSubmit);
                newVersion.setNumberOfVersion(oldAssignmentSubmit.getmVersions().size() + 1);
                oldAssignmentSubmit.addVersion(newVersion);
                if(!oldAssignmentSubmit.getStudent().equals(assignmentStudent)){
                    Student studentOnOldAssignment=oldAssignmentSubmit.getStudent();
                    List<AssignmentSubmit> studentOnAssignmentSubmit=studentOnOldAssignment.getmAssignmentSubmit();
                    studentOnAssignmentSubmit.remove(oldAssignmentSubmit);
                    studentOnOldAssignment.setmAssignmentSubmit(studentOnAssignmentSubmit);
                    userData.updateUser(assignmentStudent);
                }
                oldAssignmentSubmit.setStudent(assignmentStudent);
                assignmentStudent.addUkol(oldAssignmentSubmit);
                assignmentData.addVersion(newVersion);
                assignmentData.updateAssignmentSubmit(oldAssignmentSubmit);
            }
        } else {
            List<AssignmentSubmit> result = assignmentData.getAssignmentSubmitByContext(curAssignmentSubmit.getAssignment(),
                    curAssignmentSubmit.getStudent(), null);
            if (result.isEmpty()) {
                assignmentData.addAssignmentSubmit(curAssignmentSubmit);
            } else {
                AssignmentSubmit oldAssignmentSubmit = result.get(0);
                oldAssignmentSubmit.setDateOfSubmit(curAssignmentSubmit.getDateOfSubmit());
                oldAssignmentSubmit.setStateOfTask(curAssignmentSubmit.getStateOfTask());
                Version newVersion = curAssignmentSubmit.getmVersions().get(0);
                newVersion.setAssignmentSubmit(oldAssignmentSubmit);
                newVersion.setNumberOfVersion(oldAssignmentSubmit.getmVersions().size() + 1);
                oldAssignmentSubmit.addVersion(newVersion);
                assignmentData.addVersion(newVersion);
                assignmentData.updateAssignmentSubmit(oldAssignmentSubmit);
            }
        }

        Kantor kantorOnAssignment = curAssignmentSubmit.getAssignment().getTemplate().getKantor();
        mail.notifySendedAssignment(kantorOnAssignment, curAssignmentSubmit);
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<AssignmentSubmit> getAssignmentsToRateByContext(List<Student> mStudents, List<Subject> subjects,
        List<AssignmentSubmit.State> states) throws BusinessRuleException {
        validator.validateStudents(mStudents);
        validator.validate(subjects);
        List<AssignmentSubmit> result = new ArrayList<AssignmentSubmit>();
        result.addAll(assignmentData.getAssignmentSubmitByContext(mStudents, states, subjects));
        result.addAll(assignmentData.getAssignmentTeamToRateByContext(mStudents, subjects, states));
        return result;
    }

    @RolesAllowed("kantor")
    @Override
    public void rateTask(AssignmentSubmit taskToRate) throws BusinessRuleException,TaskProException{  
            validator.validateRate(taskToRate);
            taskToRate.setDateOfEvaluation(Maintance.getTodayDate());          
            assignmentData.updateAssignmentSubmit(taskToRate);
            mail.notifyRateUser(taskToRate.getAssignment().getTemplate().getKantor(), taskToRate);
       
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<Assignment> getAssignmentToSubmitByContextFilter(Student currStudent, List<Assignment.Type> types,
            List<AssignmentSubmit.State> states, boolean submitTask, boolean unSubmitTask) {
        List<Assignment> resultList = new ArrayList<Assignment>();
        Iterator typeIt = types.iterator();
        while (typeIt.hasNext()) {
            String currType = (String) typeIt.next();
            if (currType.equals("STUDENT")) {
                if (submitTask) {
                    resultList.addAll(assignmentData.getAssignmentSubByContext(currStudent, states, true));
                }
                if (unSubmitTask) {
                    resultList.addAll(assignmentData.getUnSubmitAssingnment(currStudent, true, null));
                    resultList.addAll(assignmentData.getUnSubmitAssignment(currStudent.getmGroups(), currStudent, true, null));
                }
            } else if (currType.equals("TEAM")) {
                if (submitTask) {
                    resultList.addAll(assignmentData.getAssignmentSubByTeam(currStudent, states, true));
                }
                if (unSubmitTask) {
                    resultList.addAll(assignmentData.getUnSubmitTeamAssignmet(currStudent, true, null));
                }
            }
        }
        return resultList;
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<AssignmentSubmit> getAssignmentSubmitsByContext(Student student, List<Subject> subjects, List<State> stateOfSubmit, List<Type> typeOfAssignment) throws BusinessRuleException {
        validator.validate(student);
        List<AssignmentSubmit> assignmentSubmits = new ArrayList<AssignmentSubmit>();
        List<Student> studentList = new ArrayList<Student>();
        if ((subjects.isEmpty()) || stateOfSubmit.isEmpty() || typeOfAssignment.isEmpty()) {
            return assignmentSubmits;
        }
        studentList.add(student);
        Iterator stateIt = typeOfAssignment.iterator();
        while (stateIt.hasNext()) {
            Type currType = (Type) stateIt.next();
            if (currType == Type.STUDENT) {
                assignmentSubmits.addAll(assignmentData.getAssignmentSubmitByContext(studentList, stateOfSubmit, subjects));
            } else if (currType == Type.TEAM) {
                assignmentSubmits.addAll(assignmentData.getAssignmentTeamToRateByContext(studentList, subjects, stateOfSubmit));
            }
        }
        return assignmentSubmits;
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<Assignment> getUnSubmitTeamAssignmet(Student studentInTeam, Subject subject) {
        return assignmentData.getUnSubmitTeamAssignmet(studentInTeam, false, subject);
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<Assignment> getUnSubmitPersonalAssignmet(Student student, Subject subject) {
        List<Assignment> resultList = new ArrayList<Assignment>();
        resultList.addAll(assignmentData.getUnSubmitAssignment(student.getmGroups(), student, false, subject));
        resultList.addAll(assignmentData.getUnSubmitAssingnment(student, false, subject));
        return resultList;
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<AssignmentSubmit> getSubmitTeamAssignmet(Student studentInTeam, List<AssignmentSubmit.State> states, Subject subject) {
        List<Student> mStudents = new ArrayList<Student>();
        mStudents.add(studentInTeam);
        List<Subject> subjects = new ArrayList<Subject>();
        subjects.add(subject);
        return assignmentData.getAssignmentTeamToRateByContext(mStudents, subjects, states);
    }
    /**
     * {@inheritDoc }
     */
    @RolesAllowed({"kantor", "student"})
    @Override
    public List<AssignmentSubmit> getSubmitPersonalAssignmet(Student student, List<AssignmentSubmit.State> states, Subject subject) {
        List<Student> mStudents = new ArrayList<Student>();
        mStudents.add(student);
        List<Subject> subjects = new ArrayList<Subject>();
        subjects.add(subject);
        return assignmentData.getAssignmentSubmitByContext(mStudents, states, subjects);
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<Assignment> getAssignmetsByKantorSubject(List<Subject> subjects, Kantor kantor) throws BusinessRuleException {
        validator.validate(kantor);
        validator.validate(subjects);
        return assignmentData.getAssignmentsByKantorAndSubject(kantor, subjects);
    }

    @RolesAllowed("kantor")
    @Override
    public void updateAssignmnet(Assignment assignment) throws BusinessRuleException {
        validator.validate(assignment); 
        List<AssignmentSubmit> assignmentSubmits = assignmentData.getAssignmentSubmitsByAssignment(assignment);
        if (assignmentSubmits.isEmpty()) {
            assignmentData.updateAssignment(assignment);
        } else {
            int beforeScore = assignmentSubmits.get(0).getAssignment().getMaxScore();
            int difference = beforeScore - assignment.getMaxScore();
            if (difference == 0) {
                assignmentData.updateAssignment(assignment);
            } else {
                Iterator asSIt = assignmentSubmits.iterator();
                while (asSIt.hasNext()) {
                    AssignmentSubmit currAs = (AssignmentSubmit) asSIt.next();
                    currAs.setGrade(currAs.getGrade() - difference);
                    if (currAs.getGrade() < 0) {
                        currAs.setGrade(0);
                    }
                    assignmentData.updateAssignmentSubmit(currAs);
                }
                assignmentData.updateAssignment(assignment);
            }
        }
    }

    @RolesAllowed({"kantor", "student"})
    @Override
    public List<AssignmentSubmit> getAssignmentSubmitsByAssignment(Assignment assignment) throws BusinessRuleException {
        // validator.validate(assignment);
        return assignmentData.getAssignmentSubmitsByAssignment(assignment);
    }

    @RolesAllowed("kantor")
    @Override
    public void removeAssignment(List<Assignment> assignmentsToRemove) throws BusinessRuleException {
        validator.validateRemove(assignmentsToRemove);
        Iterator assignmentIt = assignmentsToRemove.iterator();
        while (assignmentIt.hasNext()) {
            assignmentData.removeAssignment((Assignment) assignmentIt.next());
        }
    }

    @Override
    public List<AssignmentSubmit> getAssignmentSubmitsByKontext(Kantor kantorOnAssignmet, List<Subject> subjects, List<AssignmentSubmit.State> state) throws BusinessRuleException {
        validator.validate(kantorOnAssignmet);
        return assignmentData.getAssignmentSubmitsByKontext(kantorOnAssignmet, subjects, state);
    }

    @Override
    public Template getTemplateById(long id) {

        return assignmentData.getTemplateById(id);

    }

    @Override
    public void removeTemplate(Template template) throws BusinessRuleException {
        validator.validateRemove(template);
        List<TemplateFile> filesWithTemplate = template.getTemplateFiles();
        template.setTemplateFiles(null);
        Iterator fileIt = filesWithTemplate.iterator();
        while (fileIt.hasNext()) {
            TemplateFile fileToRemove = (TemplateFile) fileIt.next();
            fileToRemove.setTemplate(null);
            assignmentData.removeTemplateFile(fileToRemove);
        }
        assignmentData.removeTemplate(template);
    }

    @Override
    public void removeAssignmen(Assignment assignmentToRemove) throws BusinessRuleException {
        validator.validateRemove(assignmentToRemove);
        if(assignmentToRemove.getType()==Assignment.Type.TEAM){
            List<Team> teamsOnAssignment=assignmentToRemove.getmTeams();
            Iterator teamIt=teamsOnAssignment.iterator();
            while(teamIt.hasNext()){
                Team currTeam=(Team) teamIt.next();
                currTeam.removeAssignment(assignmentToRemove);
                teamData.updateTeam(currTeam);
            }
            
        }
        else if(assignmentToRemove.getType()==Assignment.Type.GROUP){
            List<GroupTaskPro> groupOnAssignment=assignmentToRemove.getmGroups();
            Iterator groupIt=groupOnAssignment.iterator();
            while(groupIt.hasNext()){
                GroupTaskPro currGroup= (GroupTaskPro) groupIt.next();
                currGroup.removeAssignment(assignmentToRemove);
                groupData.updateGroup(currGroup);
            }
            
        }
        else if(assignmentToRemove.getType()==Assignment.Type.STUDENT){
            List<Student> studentsOnAssignment=assignmentToRemove.getmStudents();
            Iterator studentIt=studentsOnAssignment.iterator();
            while(studentIt.hasNext()){
                Student currStudent= (Student) studentIt.next();
                currStudent.removeAssignment(assignmentToRemove);
                userData.updateUser(currStudent);
            }       
        }
        assignmentData.removeAssignment(assignmentToRemove);
    }
}